/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "ScanMethod6ND.h"

//---------------------------------------------------------------------------

ScanMethod6ND::ScanMethod6ND(void)
{
	myType = __wmt6ND;

	for(int i=0; i<16; i++)
		ia6lo.u.Byte[i] = 0; // will be completed in the "InternalInit" function
}

ScanMethod6ND::~ScanMethod6ND(void)
{
}

//---------------------------------------------------------------------------

int ScanMethod6ND::InternalInit(void)
{
	if(workPhase!=__wptArp) return MSR_ERR_PHASE;

	winPcap->GetIPv6UnicastAddress(&ia6lo);

	return MSR_ERR_OK;
}

//--------------------------------------------------------------------

bool ScanMethod6ND::FillMacFromCache(MFullAddress* addrIn)
{
	in6_addr ia6;
	BYTE macAddr[6];

	if(addrIn==NULL) return false;

	addrIn->addrIPv6.Get(&ia6);

/*
	// link local unicast address ?
	if(addrIn->addrIPv6.IsLinkLocalUnicastAddress()){
		winPcap->FillSrcMAC(addrIn->addrMAC.addr);
		addrIn->gwMAC = false;
		addrIn->alive = false;
		return true;
	}
*/

	// local unicast address ?
	if(addrIn->addrIPv6.IsEqualWith(&ia6lo)){
		winPcap->FillSrcMAC(addrIn->addrMAC.addr);
		addrIn->gwMAC = false;
		addrIn->alive = true;
		return true;
	}

	// multicast address ?
	if(addrIn->addrIPv6.IsMulticastAddress()){
		// is a multicast address
		macAddr[0] = 0x33;
		macAddr[1] = 0x33;
		macAddr[2] = ia6.u.Byte[12];
		macAddr[3] = ia6.u.Byte[13];
		macAddr[4] = ia6.u.Byte[14];
		macAddr[5] = ia6.u.Byte[15];
		addrIn->addrMAC.LoadFromBytePtr(macAddr);

		addrIn->gwMAC = false;
		addrIn->alive = true;

		return true;
	}

	MArpCacheRec arpCacheRec;

	if(config->xArpCacheOp!=__acoIgnore && config->xArpCacheOp!=__acoUpdOnly){
		if(arpCacheList!=NULL){
			if(arpCacheList->GetRecordByIPv6(&ia6, &arpCacheRec)){
				if(!arpCacheRec.addrMAC.IsEmpty()){
					addrIn->addrMAC.LoadFromBytePtr(&arpCacheRec.addrMAC.addr[0]);
					addrIn->gwMAC = false;
					addrIn->alive = false;
					return true;
				}
			}
		}
	}

	return false;
}

//---------------------------------------------------------------------------

int ScanMethod6ND::InternalExecute(void)
{
	int i, cnt;
	ScanPacket* pack;
	MFullAddress* addr;

	if(addrList==NULL) return MSR_ERR_PARAMETER;

	cnt = addrList->GetSize();
	for(i=0; i<cnt && !stopStopStop; i++){
		if(PacketsUsed()>0){
			CheckResponses();
			CheckDeaths();
		}

		addr = addrList->GetByIdx(i);
		if(!addr->addrIPv6.IsEmpty()){
			// it is an IPv6 address
			if(!FillMacFromCache(addr)){
				if(config->xMinDelay>0){
					if(lastScanTime!=0){
						waitDone = false;
						while(!waitDone){
							CheckResponses();
							SleepEx(1, TRUE);
							scanTime = GetTickCount();
							scanTime = ComputeDeltaTime(lastScanTime, scanTime);
							if(scanTime>=config->xMinDelay)
								waitDone = true;
						}
					}
					lastScanTime = GetTickCount();
				}

				pack = FindFirstUnusedPacket();
				while(pack==NULL){
					SleepEx(waitTime, TRUE);
					CheckResponses();
					CheckDeaths();
					pack = FindFirstUnusedPacket();
				}

				if(pack!=NULL && addr!=NULL){
					pack->Initialize(true);
					pack->sTime       = GetTickCount();
					addr->addrIPv6.Get(&pack->addr6);
					pack->port        = 0;
					pack->methodType  = myType;
					pack->fullAddress = addr;

					if(!SendIPv6NetworkSolicitation(pack))
						pack->Initialize(false);

					if((packSent % notifTcp) == 0 && packSent>0)
						if(hWnd!=NULL)
							PostMessage(hWnd, CHS_MSG_PACKETS_SENT, 0, (LPARAM)packSent);
				}
			}
		}
	}

	// wait until the end
	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	return MSR_ERR_OK;
}

//---------------------------------------------------------------------------

bool ScanMethod6ND::SendIPv6NetworkSolicitation(ScanPacket* packIn)
{
	BYTE srcMAC[6];
	in_addr6 srcIPv6;
	in_addr6 dstIPv6;
	BYTE options[8];

	if(packIn==NULL) return false;

	winPcap->GetIPv6UnicastAddress(&srcIPv6);

//	memcpy(&(packIn->paramIA), &srcIPv6, sizeof(in_addr6));

	winPcap->FillSrcMAC(&srcMAC[0]);

	options[0] = 0x01;
	options[1] = 0x01;
	options[2] = srcMAC[0];
	options[3] = srcMAC[1];
	options[4] = srcMAC[2];
	options[5] = srcMAC[3];
	options[6] = srcMAC[4];
	options[7] = srcMAC[5];

	memcpy(&dstIPv6, &(packIn->addr6), sizeof(in_addr6));

	if(!packetBuilderEx6->BuildPacketICMPNeighborSolicitation(&srcIPv6, &dstIPv6, 0, 0, 255, options, 8))
		return false;

	if(!packetBuilderEx6->AddEthernetHeaderForIPNS(srcMAC, &dstIPv6))
		return false;

	return SendPacket(packetBuilderEx6->GetPacket(), (int)packetBuilderEx6->GetPacketSize());
}

//---------------------------------------------------------------------------

void ScanMethod6ND::ProcessNeighborAdvertisement(BYTE* dataIn, int sizeIn, BYTE* srcIP, BYTE* srcMAC)
{
	ScanPacket      *pack;
	ICMP6NeighborPG *icmph;
	in_addr6        ia;
	MFullAddress    *addr;

	if(sizeIn<sizeof(ICMP6NeighborPG)) return;

	memcpy(ia.u.Byte, srcIP, 16);

	pack = FindPacket6(&ia, 0, myType);
	if(pack==NULL) return;

	icmph = (ICMP6NeighborPG*)dataIn;
	if(!ICMPv6NeighborIsSolicited(icmph->Reserved))
		return;

	addr = pack->fullAddress;
	if(addr!=NULL){
		addr->addrMAC.LoadFromBytePtr(srcMAC);
		addr->gwMAC = false;
		addr->alive = true;
	}

	pack->Initialize(false);
}

//---------------------------------------------------------------------------
